#pragma once

#include <exception>
#include <string>
#include <sstream>

#include "base.h"

NS_CLPP_BEGIN

class Exception : public std::exception
{
public:
	Exception(const char * what, const char * file, int line, cl_int clerror = CL_SUCCESS) :
		exception(what),
		m_FileName(file),
		m_LineNumber(line),
		m_CL_Error(clerror)
	{
	}

	cl_int GetCLErrorNumber()
	{
		return this->m_CL_Error;
	}

	int GetLine()
	{
		return this->m_LineNumber;
	}

	const char * GetFileName()
	{
		return this->m_FileName.c_str();
	}

private:
	cl_int m_CL_Error;

	int m_LineNumber;
	std::string m_FileName;
};

class BuildException : public Exception
{
public:
	BuildException(const char * what, const char * file, int line, std::string buildLog, cl_int clerror = CL_SUCCESS) :
		Exception(what, file, line, clerror),
		m_BuildLog(buildLog)
	{
	}

	const std::string & GetBuildLog()
	{
		return m_BuildLog;
	}

private:
	std::string m_BuildLog;
};

#ifndef THROW_CLPP_EXCEPTION
#define THROW_CLPP_EXCEPTION(error_info) { throw clpp::Exception(error_info, __FILE__, __LINE__); }
#endif

#ifndef IS_CL_SUCCESS
#define IS_CL_SUCCESS(cl_return_value) (cl_return_value == CL_SUCCESS)
#endif


#ifndef TEST_AND_THROW_CLPP_EXCEPTION
#define TEST_AND_THROW_CLPP_EXCEPTION(cl_error, error_info) if(!IS_CL_SUCCESS(cl_error)) \
	{ throw clpp::Exception(error_info, __FILE__, __LINE__, cl_error); }
#endif

#ifndef TEST_AND_THROW_CLPP_BUILD_EXCEPTION
#define TEST_AND_THROW_CLPP_BUILD_EXCEPTION(cl_error, error_info, logText) if(!IS_CL_SUCCESS(cl_error)) \
	{ throw clpp::BuildException(error_info, __FILE__, __LINE__, logText, cl_error); }
#endif

#ifndef TEST_GIVEN_EXPRESSION_AND_THROW_CLPP_EXCEPTION
#define TEST_GIVEN_EXPRESSION_AND_THROW_CLPP_EXCEPTION(expression, error_info) \
	if ((expression)) \
	{ throw clpp::Exception(error_info, __FILE__, __LINE__); }
#endif

NS_CLPP_END